<template lang="html">
  <d2-container>
    <page-header
      ref="pageHeader"
      slot="header"
      @success="headerSuccess"
      @startLoading="loading = true"
      @closeLoading="loading = false"
      @doSearch="refreshData"
    />

    <!-- 中间内容容器 -->
    <div v-loading="loading" class="loading-wrapper menu-wrapper">
      <el-button v-has="'menuAdd'" icon="el-icon-plus" type="primary" @click="doAdd"
        >新增</el-button
      >
      <el-button v-has="'menuHide'" :disabled="!checkedMenuIdList.length" @click="doHide"
        >隐藏</el-button
      >
      <el-button v-has="'menuShow'" :disabled="!checkedMenuIdList.length" @click="doShow"
        >显示</el-button
      >

      <el-table
        :key="tempRefreshKey"
        ref="treeTable"
        :data="tableData"
        style="width: 100%"
        row-key="id"
        stripe
        class="mt10"
        tooltip-effect="dark"
        :lazy="lazyFlag"
        :default-expand-all="!lazyFlag"
        :load="loadTreeData"
        :tree-props="{ children: 'childList', hasChildren: 'hasChildren' }"
        @select-all="selectAll"
      >
        <el-table-column align="center" prop="isChecked" type="selection" width="50">
          <el-checkbox
            v-model="row.isChecked"
            slot-scope="{ row }"
            :indeterminate="row.isIndeterminate"
            @change="(flag) => handleRowChange({ flag, row })"
          />
        </el-table-column>
        <el-table-column prop="name" label="菜单名称" show-overflow-tooltip />
        <el-table-column prop="url" label="资源路径" show-overflow-tooltip />
        <el-table-column prop="perm" label="权限标识" show-overflow-tooltip align="center" />
        <el-table-column prop="frontPerm" label="按钮标识" show-overflow-tooltip align="center" />
        <el-table-column label="菜单类型" width="100" align="center">
          <span slot-scope="{ row }"> {{ row.type | getTypeText }}</span>
        </el-table-column>
        <el-table-column prop="state" label="菜单状态" width="100" align="center">
          <span slot-scope="{ row }">{{ row.state === "1" ? "显示" : "隐藏" }}</span>
        </el-table-column>
        <el-table-column prop="icon" label="图标" width="50" align="center">
          <i slot-scope="{ row }" class="icon iconfont" :class="row.icon"></i>
        </el-table-column>
        <el-table-column prop="sortBy" label="排序" width="80" align="center" />
        <el-table-column label="操作" width="140" fixed="right">
          <div slot-scope="{ row }">
            <el-button
              v-if="['0', '1', '3'].includes(row.type)"
              v-has="'menuChildAdd'"
              type="text"
              @click="doAddChild(row)"
              >新增</el-button
            >
            <el-button v-has="'menuEdit'" type="text" @click="doEdit(row)">编辑</el-button>
            <el-button v-has="'menuDel'" type="text" class="danger" @click="doDel(row)"
              >删除</el-button
            >
          </div>
        </el-table-column>
      </el-table>
    </div>

    <!-- 新增、编辑 -->
    <menu-draw
      :dialogVisible.sync="menuDrawVisible"
      :currentRowObj="currentRowObj"
      :isEdit="isEdit"
      :isChildAdd="isChildAdd"
      @success="refreshData"
    />
  </d2-container>
</template>

<script>
import PageHeader from "./components/page-header";
import MenuDraw from "./components/menu-draw";
import { MenuList, MenuDel, MenuBatch } from "api/sys/menu";
import tableMixinKeepAlive from "mixins/tableMixinKeepAlive";
import { cloneDeep } from "lodash";

export default {
  name: "system-menu",
  components: {
    PageHeader,
    MenuDraw,
  },
  filters: {
    getTypeText(val) {
      return {
        0: "路由",
        1: "菜单",
        2: "按钮",
        3: "应用",
        4: "tabBar",
        5: "外部链接",
      }[val];
    },
  },
  mixins: [tableMixinKeepAlive],
  data() {
    return {
      // 列表接口名
      getTableListSyncMethod: MenuList,
      // 菜单开关
      menuDrawVisible: false,
      // 直接加载数据
      loadDataDirectory: false,
      // 复制菜单开关
      menuCopyDialogVisible: false,
      // 是否为编辑
      isEdit: false,
      // 是否为列表中的编辑
      isChildAdd: false,
      // 当前行数据
      currentRowObj: null,
      // 选中的数据menuId
      checkedMenuIdList: [],
      // 是否为分页的开关
      isPageData: false,
      // 是否为懒加载
      lazyFlag: false,
      // 设置刷新table的key
      tempRefreshKey: Date.now(),
    };
  },

  watch: {
    tableData: {
      deep: true,
      handler() {
        this.toggleRowSelection();
        this.getCheckedMenuIdList();
      },
    },
  },

  methods: {
    /**
     * 获取所有选中的数据
     */
    getCheckedMenuIdList() {
      const { checkedArr } = this.getCheckedAndAllData();
      this.checkedMenuIdList = checkedArr.map((item) => item.menuId);
    },
    /**
     * 获取最后一层选中的数据
     */
    getAllCheckedChildren({ tableData, checkedArr = [] } = {}) {
      tableData = tableData || this.tableData;
      tableData.map((item) => {
        const { menuId, isChecked, childList } = item;
        if (childList && childList.length) {
          this.getAllCheckedChildren({ tableData: childList, checkedArr });
        } else {
          if (isChecked) {
            checkedArr.push(menuId);
          }
        }
      });
      return checkedArr;
    },
    /**
     * 顶部的选择事件
     */
    selectAll() {
      this.setAllChecked();
    },
    /**
     * 设置所有选中、所有不选中
     */
    setAllChecked({ tableData, flag } = {}) {
      tableData = tableData || this.tableData;
      tableData.map((item) => {
        const { childList } = item;
        if (flag === undefined) {
          const { checkedArr, allDataArr } = this.getCheckedAndAllData();
          flag = checkedArr.length !== allDataArr.length;
        }
        item.isChecked = flag;
        item.isIndeterminate = false;
        if (childList && childList.length) {
          this.setAllChecked({ tableData: childList, flag });
        }
      });
    },
    /**
     * 获取选中的数据、全部数据
     */
    getCheckedAndAllData({ tableData, checkedArr = [], allDataArr = [] } = {}) {
      tableData = tableData || this.tableData;
      tableData.map((item) => {
        const { childList, isChecked } = item;
        if (isChecked) {
          checkedArr.push(item);
        }
        allDataArr.push(item);
        if (childList && childList.length) {
          this.getCheckedAndAllData({
            tableData: childList,
            checkedArr,
            allDataArr,
          });
        }
      });
      return { checkedArr, allDataArr };
    },
    toggleRowSelection({ tableData } = {}) {
      tableData = tableData || this.tableData;
      tableData.map((item) => {
        const { isChecked, childList } = item;
        this.$refs.treeTable.toggleRowSelection(item, isChecked);
        if (childList && childList.length) {
          this.toggleRowSelection({ tableData: childList });
        }
      });
    },
    /**
     * 顶部搜索框回调
     */
    headerSuccess(val) {
      const { searchObj } = val;
      this.searchObj = searchObj;
      this.refreshData();
      // if (Object.keys(obj).length) {
      //   const { affiliationAppOptions, affiliationAppTypeOptions } = obj;
      //   this.affiliationAppOptions = affiliationAppOptions;
      //   this.affiliationAppTypeOptions = affiliationAppTypeOptions;
      // }
    },
    /**
     * 新增
     */
    doAdd() {
      // const { affiliationApp, affiliationAppType } = this.$refs.pageHeader.getSearchParams();
      this.currentRowObj = {
        sortBy: this.tableData.length + 1 || 1,
      };
      this.menuDrawVisible = true;
      this.isEdit = false;
      this.isChildAdd = false;
    },
    /**
     * 新增子菜单
     */
    doAddChild(row) {
      this.isEdit = false;
      this.isChildAdd = true;
      this.currentRowObj = row;
      this.menuDrawVisible = true;
    },
    /**
     * 编辑
     */
    doEdit(row) {
      this.currentRowObj = { ...row, flag: true, isEdit: true };
      this.isEdit = true;
      this.isChildAdd = false;
      this.menuDrawVisible = true;
    },
    /**
     * 删除
     */
    async doDel(row) {
      await this.$myConfirm("删除本条数据");
      try {
        const { id } = row;
        await MenuDel(id);
        this.currentRowObj = { ...row, flag: true };
        this.$message.success("菜单删除成功");
        this.refreshData();
      } catch (e) {
        console.log(e);
      }
    },
    /**
     * 刷新
     */
    refreshData() {
      const { state, name } = this.searchObj;
      let tempObj = { ...(state && { state }), ...(name && { name }) };
      const flag = Number(!!Object.keys(tempObj).length);
      this.lazyFlag = flag === 0;
      this.refreshTableData();
    },
    /**
     * 刷新table数据
     */
    refreshTableData() {
      if (this.currentRowObj) {
        const { flag, parentMenuId, index, menuId, isChecked, parentId, parentMenuName, isEdit } =
          this.currentRowObj;
        if (!flag) {
          if (!menuId) {
            this.getTableData();
          } else {
            this.refreshTreeData({
              menuId,
              treeIndex: index,
              isChecked,
              name: parentMenuName,
            });
          }
        } else {
          if (parentId === 0) {
            this.getTableData();
          } else {
            // 更新父级数据
            this.refreshTreeData({
              id: parentMenuId,
              treeIndex: this.getParentIndex({ index }),
              name: parentMenuName,
              isEdit,
            });
          }
        }
      } else {
        this.getTableData();
      }
    },
    /**
     * 获取父级的index
     */
    getParentIndex({ index }) {
      let tempArr = index.split("-");
      tempArr.pop();
      return tempArr.join("-");
    },
    /**
     * 加载树节点数据
     */
    async refreshTreeData({ id, treeIndex, name, isEdit = false }) {
      const data = await this.loadChildData({
        id,
        treeIndex,
        isEdit,
        name,
      });
      // 更新数据
      this.$set(this.$refs.treeTable.store.states.lazyTreeNodeMap, id, data);
      await this.$nextTick();
      // 展开树
      const obj = this.$refs.treeTable.store.states.treeData[id];
      if (obj) {
        obj.loaded = true;
        obj.expanded = true;
      }
    },
    /**
     * 格式化返回数据，mixin函数中用到
     */
    tableDataFormat(tableData) {
      tableData.map((item, index) => {
        const { parentId, subCount, childList, itemIndex } = item;
        if (this.lazyFlag) {
          item.hasChildren = childList ? !!subCount || !!childList.length : !!subCount;
        }
        item.isChecked = false;
        item.isIndeterminate = false;
        item.parentMenuId = parentId;
        if (!itemIndex) {
          item.index = (index + 1).toString();
        }
        if (childList && childList.length) {
          this.tableDataFormat(childList);
        }
      });
      return tableData;
    },
    /**
     * 格式化树形数据(非懒加载时用到)
     */
    setAllDataHasChildren({ tableData } = {}) {
      tableData = tableData || this.tableData;
      tableData.map((item) => {
        const { parentId, subCount, childList } = item;
        item.hasChildren = !!subCount || !!childList.length;
        item.isChecked = false;
        item.isIndeterminate = false;
        item.parentMenuId = parentId;
        if (childList && childList.length) {
          this.setAllDataHasChildren({ tableData: childList });
        }
      });
    },
    /**
     * 节点点击事件
     */
    async loadTreeData(tree, treeNode, resolve) {
      const { id, name, isChecked, index: treeIndex } = tree;
      try {
        const data = await this.loadChildData({
          id,
          name,
          isChecked,
          treeIndex,
        });
        resolve(data);
      } catch (e) {
        console.log(e);
      }
    },
    /**
     * 加载子集数据
     */
    async loadChildData({ id, isChecked, treeIndex, name, isEdit }) {
      try {
        const data = await MenuList({ parentId: id });
        let tempObj = {};
        if (isEdit) {
          // 编辑数据处理
          const { childList } = this.getOldTableDataByMenuId({
            id,
          });
          childList.map((item) => {
            const { id, isChecked, isIndeterminate } = item;
            tempObj[id] = { isChecked, isIndeterminate };
          });
        }
        // 正常操作的数据
        data.map((item, index) => {
          const { subCount, id: curMenuId } = item;
          if (isEdit) {
            // 编辑数据设置(和原始数据比对，取原始数据的isChecked，isIndeterminate字段值)
            const curObj = tempObj[curMenuId];
            const { isChecked: oldIsChecked, isIndeterminate } = curObj;
            item.isChecked = oldIsChecked;
            item.isIndeterminate = isIndeterminate;
          } else {
            // 正常数据设置
            item.isChecked = isChecked; // 取父级数据
            item.isIndeterminate = false; // 全部默认设置为false
          }
          item.index = `${treeIndex}-${index + 1}`;
          item.hasChildren = !!subCount;
          item.parentMenuId = id;
          item.parentMenuName = name;
        });
        // 手动设置tableData的数据
        this.setChildrenData({
          parentMenuId: id,
          childrenData: data,
          isEdit,
        });
        return data;
      } catch (e) {
        console.log(e);
      }
    },
    /**
     * 编辑时，获取父级对象的原始数据，用于和新数据的比对
     * 设置原始的isChecked与isIndeterminate状态
     */
    getOldTableDataByMenuId({ id, tableData }) {
      // TODO: 现在值获取的有点多了，现在先满足编辑的状态
      // 删除的时候，需要用到父级的数据
      let tempObj = {};
      tableData = tableData || this.tableData;
      for (let i = 0; i < tableData.length; i++) {
        const { id: curMenuId, childList } = tableData[i];
        if (curMenuId === id) {
          tempObj = tableData[i];
          break;
        }
        if (childList && childList.length) {
          tempObj = this.getOldTableDataByMenuId({
            id,
            tableData: childList,
          });
        }
      }
      return cloneDeep(tempObj);
    },
    /**
     * 设置tableData的children数据
     * @param {String} parentMenuId  [父级的menuId]
     * @param {Array} childrenData [要设置的子级数据]
     */
    setChildrenData({ parentMenuId, childrenData, tableData }) {
      tableData = tableData || this.tableData;
      tableData.map((item) => {
        const { id, childList } = item;
        if (id === parentMenuId) {
          // this.$set(item, 'childList', childrenData)
          item.childList = childrenData;
        }
        if (childList && childList.length) {
          this.setChildrenData({
            parentMenuId,
            childrenData,
            tableData: childList,
          });
        }
      });
    },
    /**
     * row的选择事件，非header的选择
     */
    handleRowChange({ flag, row }) {
      const { id, parentMenuId } = row;
      // 向下遍历
      this.setRowCheckedDown({ currentMenuId: id, flag });
      // 向上遍历
      this.setRowCheckedUp({ parentMenuId });
      this.$refs.treeTable.clearSelection();
    },
    /**
     * 向上设置选中状态
     */
    setRowCheckedUp({ tableData, parentMenuId }) {
      tableData = tableData || this.tableData;
      tableData.map((item) => {
        const { id, childList, parentMenuId: itemParentMenuId } = item;
        const flag = childList && childList.length;
        if (id === parentMenuId) {
          // 当前
          if (flag) {
            let tempCheckedArr = [];
            let tempIndeterminateArr = [];
            childList.map((item1) => {
              const { isChecked, isIndeterminate } = item1;
              isChecked && tempCheckedArr.push(item1.isChecked);
              isIndeterminate && tempIndeterminateArr.push(item1.isIndeterminate);
            });
            const { length: checkedLen } = tempCheckedArr;
            const { length } = childList;
            switch (true) {
              case checkedLen === length:
                item.isChecked = true;
                item.isIndeterminate = false;
                break;
              case checkedLen < length && checkedLen > 0:
                item.isChecked = false;
                item.isIndeterminate = true;
                break;
              case checkedLen === 0:
                item.isChecked = false;
                item.isIndeterminate = !!tempIndeterminateArr.length;
                break;
            }
          }
          // 从头再来
          if (itemParentMenuId !== 0) {
            this.setRowCheckedUp({
              parentMenuId: itemParentMenuId,
            });
          }
        } else {
          if (flag) {
            // 向下
            this.setRowCheckedUp({ tableData: childList, parentMenuId });
          }
        }
      });
    },
    /**
     * 设置table数据的选中状态
     */
    setRowCheckedDown({ tableData, currentMenuId, flag }) {
      tableData = tableData || this.tableData;
      tableData.map((item) => {
        const { id, childList } = item;
        const childrenFlag = childList && childList.length;
        if (id === currentMenuId) {
          item.isChecked = flag;
          item.isIndeterminate = false;
          if (childrenFlag) {
            // 向下，统一所有子集的选中状态
            this.setAllCheckedStatus({ list: childList, flag });
          }
        } else {
          if (childrenFlag) {
            this.setRowCheckedDown({
              tableData: childList,
              currentMenuId,
              flag,
            });
          }
        }
      });
    },
    /**
     * 统一设置所有子级的状态：显示、隐藏
     */
    setAllCheckedStatus({ list, flag }) {
      list.map((item) => {
        item.isChecked = flag;
        item.isIndeterminate = false;
        const { childList } = item;
        if (childList && childList.length) {
          this.setAllCheckedStatus({ list: childList, flag });
        }
      });
    },
    /**
     * 隐藏
     */
    async doHide() {
      await this.$myConfirm("隐藏选中菜单");
      await MenuBatch({ actionFlag: 0, menuIdList: this.checkedMenuIdList });
      this.$message.success("操作成功");
      this.refreshData();
    },
    /**
     * 显示
     */
    async doShow() {
      await this.$myConfirm("显示选中菜单");
      await MenuBatch({ actionFlag: 1, menuIdList: this.checkedMenuIdList });
      this.$message.success("操作成功");
      this.refreshData();
    },
    /**
     * 搜索数据格式化
     */
    searchObjFormat() {
      const { state, name, ...obj } = this.searchObj;
      let tempObj = { ...(state && { state }), ...(name && { name }) };
      // tempObj.flag = Number(!!Object.keys(tempObj).length);
      return {
        params: {
          ...tempObj,
          ...obj,
        },
        parentId: 0,
      };
    },
  },
};
</script>

<style lang="scss">
.menu-wrapper {
  tr {
    td:first-child,
    th:first-child {
      .cell {
        padding-left: 0 !important;
        padding-right: 0 !important;
      }
    }
  }
}
</style>
